package com.lw.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lw.common.enums.GlobalTypeEnum;
import com.lw.common.utils.GlobalConstants;
import com.lw.common.utils.StrUtils;
import com.lw.common.vo.KeyValue;
import com.lw.common.vo.Option;
import com.lw.common.vo.RouteVO;
import com.lw.entity.TbMenu;
import com.lw.service.TbRoleMenuService;
import com.lw.service.TbUserRoleService;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.lw.mapper.TbMenuMapper;
import com.lw.service.TbMenuService;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;
import static com.lw.entity.table.TbMenuTableDef.TB_MENU;

/**
 * 菜单管理 服务层实现。
 *
 * @author lw
 * @since 2024年12月08日 07:53:39
 */
@Service
public class TbMenuServiceImpl extends ServiceImpl<TbMenuMapper, TbMenu> implements TbMenuService {

    @Resource
    private TbRoleMenuService tbRoleMenuService;

    @Resource
    private TbUserRoleService tbUserRoleService;


    @Override
    public List<RouteVO> getCurrentUserRoutes() {
        //如果是管理员,查询所有的菜单路由
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.ne(TbMenu::getType, GlobalTypeEnum.ROUTE_BUTTON.getValue());
        queryWrapper.orderBy(TbMenu::getSort);
        List<TbMenu> tbMenuList = this.list(queryWrapper);
        return buildTreeRouter(GlobalConstants.ROOT_NODE_ID, tbMenuList);
    }

    @Override
    public List<Option<Long>> listMenuOptions(boolean onlyParent) {
        QueryWrapper queryWrapper = new QueryWrapper();
        if (onlyParent) {
            queryWrapper.in(TbMenu::getType, GlobalTypeEnum.ROUTE_CATALOG.getValue(), GlobalTypeEnum.ROUTE_MENU.getValue());
        }
        queryWrapper.orderBy(TbMenu::getSort);
        List<TbMenu> tbMenuList = this.list(queryWrapper);
        return buildMenuOptionsTree(GlobalConstants.ROOT_NODE_ID, tbMenuList);
    }

    @Override
    public List<TbMenu> getMenusTree(TbMenu queryParams) {
        QueryWrapper queryWrapper = QueryWrapper.create();
        queryWrapper.like(TB_MENU.NAME.getName(), queryParams.getKeywords(), StrUtils.hasLength(queryParams.getKeywords()));
        queryWrapper.orderBy(TB_MENU.SORT.getName());
        List<TbMenu> menuList = list(queryWrapper);

        // 获取所有菜单ID
        Set<Long> menuIds = menuList.stream().map(TbMenu::getId).collect(Collectors.toSet());
        // 获取所有父级ID
        Set<Long> parentIds = menuList.stream().map(TbMenu::getParentId).collect(Collectors.toSet());
        // 获取根节点ID（递归的起点），即父节点ID中不包含在部门ID中的节点，注意这里不能拿顶级菜单 O 作为根节点，因为菜单筛选的时候 O 会被过滤掉
        List<Long> rootIds = parentIds.stream().filter(id -> !menuIds.contains(id)).toList();
        return rootIds.stream().flatMap(rootId -> buildMenuTree(rootId, menuList).stream()).collect(Collectors.toList());

    }

    @Override
    public Boolean saveMenu(TbMenu tbMenu) {
        Integer menuType = tbMenu.getType();

        if (GlobalTypeEnum.ROUTE_CATALOG.getValue().equals(menuType)) {  // 如果是目录
            String path = tbMenu.getRoutePath();
            if (tbMenu.getParentId() == 0 && !path.startsWith("/")) {
                tbMenu.setRoutePath("/" + path); // 一级目录需以 / 开头
            }
            tbMenu.setComponent("Layout");
        } else if (GlobalTypeEnum.ROUTE_EXT_LINK.getValue().equals(menuType)) {
            // 外链菜单组件设置为 null
            tbMenu.setComponent(null);
        }
        if (Objects.equals(tbMenu.getParentId(), tbMenu.getId())) {
            throw new RuntimeException("父级菜单不能为当前菜单");
        }
        String treePath = generateMenuTreePath(tbMenu.getParentId());
        tbMenu.setTreePath(treePath);

        List<KeyValue> params = tbMenu.getParamsList();
        // 路由参数 [{key:"id",value:"1"}，{key:"name",value:"张三"}] 转换为 [{"id":"1"},{"name":"张三"}]
        if (CollectionUtil.isNotEmpty(params)) {
            tbMenu.setParams(JSONUtil.toJsonStr(params.stream()
                    .collect(Collectors.toMap(KeyValue::getKey, KeyValue::getValue))));
        } else {
            tbMenu.setParams(null);
        }
        // 新增类型为菜单时候 路由名称唯一
        if (GlobalTypeEnum.ROUTE_MENU.getValue().equals(menuType)) {
            Assert.isFalse(this.exists(QueryWrapper.create()
                    .eq(TB_MENU.ROUTE_NAME.getName(), tbMenu.getRouteName())
                    .ne(TB_MENU.ID.getName(), tbMenu.getId(), tbMenu.getId() != null)
            ), "路由名称已存在");
        } else {
            // 其他类型时 给路由名称赋值为空
            tbMenu.setRouteName(null);
        }
        boolean result = this.saveOrUpdate(tbMenu);
//        if (result) {
//            // 编辑刷新角色权限缓存
//            if (tbMenu.getId() != null) {
//                roleMenuService.refreshRolePermsCache();
//            }
//        }
        // 修改菜单如果有子菜单，则更新子菜单的树路径
        updateChildrenTreePath(tbMenu.getId(), treePath);
        return result;
    }

    @Override
    public Boolean deleteMenu(Long id) {
        //删除菜单
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq(TB_MENU.ID.getName(), id);
        return this.remove(queryWrapper);
    }


    /**
     * 获取用户权限列表并且缓存
     * @param loginId 用户id
     * by lw on 二〇二五年五月十三日 23:55:38
     * @return 用户权限列表
     */
    @Cacheable(cacheNames = "permissionCache",key = "#loginId")
    @Override
    public List<String> getPermissionList(Long loginId) {
        //查询用户角色集合
        Set<Long> role = tbUserRoleService.getRoleListByUserId(loginId);
        //查询用户权限集合
        if (CollectionUtil.isNotEmpty(role)) {
           return tbRoleMenuService.getRolePermsList(role);
        }

        return List.of();
    }

    /**
     * 更新子菜单树路径
     *
     * @param id       当前菜单ID
     * @param treePath 当前菜单树路径
     */
    private void updateChildrenTreePath(Long id, String treePath) {
        List<TbMenu> children = this.list(QueryWrapper.create().eq(TB_MENU.PARENT_ID.getName(), id));
        if (CollectionUtil.isNotEmpty(children)) {
            // 子菜单的树路径等于父菜单的树路径加上父菜单ID
            String childTreePath = treePath + "," + id;
            TbMenu updateMenu = new TbMenu();
            updateMenu.setTreePath(childTreePath);
            this.update(updateMenu, QueryWrapper.create().eq(TB_MENU.PARENT_ID.getName(), id));
            for (TbMenu child : children) {
                // 递归更新子菜单
                updateChildrenTreePath(child.getId(), childTreePath);
            }
        }
    }

    /**
     * 部门路径生成
     *
     * @param parentId 父ID
     * @return 父节点路径以英文逗号(, )分割，eg: 1,2,3
     */
    private String generateMenuTreePath(Long parentId) {
        if (GlobalConstants.ROOT_NODE_ID.equals(parentId)) {
            return String.valueOf(parentId);
        } else {
            TbMenu parent = this.getById(parentId);
            return parent != null ? parent.getTreePath() + "," + parent.getId() : null;
        }
    }

    private List<TbMenu> buildMenuTree(Long parentId, List<TbMenu> menuList) {
        return CollectionUtil.emptyIfNull(menuList).stream()
                .filter(menu -> menu.getParentId().equals(parentId))
                .map(entity -> {
                    List<TbMenu> tbMenusList = buildMenuTree(entity.getId(), menuList);
                    entity.setChildren(tbMenusList);
                    return entity;
                }).toList();
    }


    private List<Option<Long>> buildMenuOptionsTree(Long parentId, List<TbMenu> tbMenuList) {
        List<Option<Long>> menuOptions = new ArrayList<>();
        for (TbMenu menu : tbMenuList) {
            if (menu.getParentId().equals(parentId)) {
                Option<Long> option = new Option<>(menu.getId(), menu.getName());
                List<Option<Long>> subMenuOptions = buildMenuOptionsTree(menu.getId(), tbMenuList);
                if (!subMenuOptions.isEmpty()) {
                    option.setChildren(subMenuOptions);
                }
                menuOptions.add(option);
            }
        }

        return menuOptions;
    }

    /**
     * 构建树形路由
     * by lw on 二〇二五年五月十三日 23:55:38
     *
     * @param rootNodeId 根节点ID
     * @param tbMenuList 菜单列表
     * @return 树形路由
     */
    private List<RouteVO> buildTreeRouter(Long rootNodeId, List<TbMenu> tbMenuList) {
        List<RouteVO> routeList = new ArrayList<>();
        for (TbMenu tbMenu : tbMenuList) {
            if (tbMenu.getParentId().equals(rootNodeId)) {
                RouteVO routeVO = toRouteVo(tbMenu);
                List<RouteVO> childrenList = buildTreeRouter(tbMenu.getId(), tbMenuList);
                if (!childrenList.isEmpty()) {
                    routeVO.setChildren(childrenList);
                }
                routeList.add(routeVO);
            }
        }
        return routeList;
    }

    private RouteVO toRouteVo(TbMenu menu) {
        RouteVO routeVO = new RouteVO();
        // 获取路由名称
        String routeName = menu.getRouteName();
        if (StrUtil.isBlank(routeName)) {
            // 路由 name 需要驼峰，首字母大写
            routeName = StringUtils.capitalize(StrUtil.toCamelCase(menu.getRoutePath(), '-'));
        }
        // 根据name路由跳转 this.$router.push({name:xxx})
        routeVO.setName(routeName);

        // 根据path路由跳转 this.$router.push({path:xxx})
        routeVO.setPath(menu.getRoutePath());
        routeVO.setRedirect(menu.getRedirect());
        routeVO.setComponent(menu.getComponent());

        RouteVO.Meta meta = new RouteVO.Meta();
        meta.setTitle(menu.getName());
        meta.setIcon(menu.getIcon());
        meta.setHidden(GlobalTypeEnum.STATE_DISABLE.getValue().equals(menu.getVisible()));
        // 【菜单】是否开启页面缓存
        if (GlobalTypeEnum.ROUTE_MENU.getValue().equals(menu.getType())
                && ObjectUtil.equals(menu.getKeepAlive(), 1)) {
            meta.setKeepAlive(true);
        }
        meta.setAlwaysShow(ObjectUtil.equals(menu.getAlwaysShow(), 1));

        String paramsJson = menu.getParams();
        // 将 JSON 字符串转换为 Map<String, String>
        if (StrUtil.isNotBlank(paramsJson)) {
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                Map<String, String> paramMap = objectMapper.readValue(paramsJson, new TypeReference<>() {
                });
                meta.setParams(paramMap);
            } catch (Exception e) {
                throw new RuntimeException("解析toRouteVo方法中参数失败", e);
            }
        }
        routeVO.setMeta(meta);
        return routeVO;
    }
}
